<?php

declare(strict_types=1);

namespace app\eslib;

class EsUtils
{

    /**
     * 获取标准消息格式
     * @param integer|boolean $status 状态，值为 0 或 1
     * @param string $msg 错误消息内容
     * @param mixed $data 成功的数据
     * @param string $code 代码
     * @return array ['status','message','data','code']
     */
    public static function stdmessage($status, $message, $data = '', $code = '')
    {
        return [
            'status' => intval($status),
            'message'  => $message,
            'data' => $data,
            'code' => $code,
        ];
    }

    /**
     * 字节转容量
     */
    public static function formatBytes($size, $digits = 2)
    {
        if (!$size) {
            return $size;
        }
        $base = 1024;
        $unit = ['B', 'KB', 'MB', 'GB', 'TB', 'PB'];
        $i = floor(log($size, $base));
        $n = count($unit);
        if ($i >= $n) {
            $i = $n - 1;
        }
        return round($size / pow($base, $i), $digits) . ' ' . $unit[$i];
    }

    /**
     * 容量转字节
     */
    public static function toBytes($size, $digits = 2)
    {
        $unit = ['K', 'M', 'G', 'T', 'P'];
        $i = 1;
        foreach ($unit as $v) {
            if (strpos($size, $v)) {
                break;
            }
            $i++;
        }
        $base = 1024;
        return doubleval($size) * pow($base, $i);
    }

    /**
     * 截短字符串
     * @param string $str 字符串
     * @param integer $len 长度
     * @return string
     */
    public static function shortText($text, $length, $lastLen = 8, $ellipsis  = '...')
    {
        if (empty($text) || mb_strlen($text) <= $length) {
            return $text;
        }
        if ($lastLen > 0) {
            $text = mb_substr($text, 0, $length - $lastLen) . $ellipsis . mb_substr($text, -$lastLen);
        } else {
            $text = mb_substr($text, 0, $length) . $ellipsis;
        }
        return $text;
    }

    /**
     * 截短 MD5（16位）
     */
    public static function shortMD5($text)
    {
        return substr(md5(strval($text)), 8, 16);
    }

    /**
     * 密码加密
     */
    public static function hashPassword($password, $salt = '')
    {
        // 截取中间16位作为签名
        return substr(md5($password . $salt), 8, 16);
    }

    /**
     * 获取不重复序列号
     * 大约是原来长度的一半,比如12位生成6位,21位生成13位
     */
    public static function hashSerialNo($prefix = '')
    {
        $time = date('y-m-d-h-i-s');
        if (is_numeric($prefix)) {
            $time = chunk_split(strval($prefix), 2, '-') . $time;
            $prefix = '';
        }
        $atime = explode('-', $time);
        foreach ($atime as $stime) {
            $itime = intval($stime);
            if ($itime < 26) {
                $prefix .= chr(65 + $itime);
                continue;
            }
            if ($itime >= 48 && $itime <= 57) {
                $prefix .= chr($itime);
                continue;
            }
            $prefix .= $stime;
        }
        return $prefix;
    }

    /**
     * 返回语义化时间
     * @param integer|string $time 时间
     * @param string $break 断点，超过断点以后的时间会直接以指定的日期格式显示
     * @param string $format 日期格式, 与$break参数结合使用
     * @param boolean $aliasable 是否允许以 昨天、前天 来代替 1 天前、2 天前
     * @return string 返回语义化时间，例如：几秒，几分，几小时，几天前，几小时前，几月前 等
     * @example 
     *      humantime(strtotime('-5 month'), 'month') 返回 2019-10-27 17:50:17
     *      humantime(strtotime('-5 month'), 'year') 返回 5 个月前
     *      humantime(strtotime('yesterday')) 返回 昨天
     *      humantime(strtotime('-2 day')); 返回 前天
     */
    public static function humantime($time, $break = '', $format = 'Y-m-d H:i:s', $aliasable = true)
    {
        if (!$time) {
            return '';
        }
        if (!is_numeric($time)) {
            $time = strtotime($time);
        }
        $text = '';
        $seconds = time() - $time;
        if ($seconds > 0) {
            $formater = array(
                'second' => ['time' => '1', 'text' => '秒'],
                'minute' => ['time' => '60', 'text' => '分钟'],
                'hour' => ['time' => '3600', 'text' => '小时'],
                'day' => ['time' => '86400', 'text' => '天', 'alias' => ['1' => '昨天', '2' => '前天']],
                'week' => ['time' => '604800', 'text' => '星期'],
                'month' => ['time' => '2592000', 'text' => '个月'],
                'year' => ['time' => '31536000', 'text' => '年'],
            );
            $prevName = '';
            foreach ($formater as $name => $data) {
                if ($seconds < intval($data['time'])) {
                    $prevData = $formater[$prevName];
                    $count = floor($seconds / intval($prevData['time']));
                    if ($aliasable && isset($prevData['alias']) && isset($prevData['alias'][strval($count)])) {
                        $text = $prevData['alias'][strval($count)];
                        break;
                    }
                    $text = $count . ' ' . $prevData['text'] . '前';
                    break;
                }
                $prevName = $name;
                if ($break && ($name == $break)) {
                    $text = date($format, $time);
                    break;
                }
            }
        } else {
            $text = date($format, $time);
        }
        return $text;
    }
    /**
     * 计算两个日期的相差天数
     * @param string $date1 日期1
     * @param string $date1 日期2
     * @return int 返回相差的天数
     */
    public static function diffDays($date1, $date2)
    {
        return abs(strtotime($date1) - strtotime($date2)) / 86400;
    }
    /**
     * 根据生日计算年龄
     * @param string $birthday 生日
     * @return int 返回年龄
     */
    public static function calcAge($birthday)
    {
        $age = empty($birthday) ? false : strtotime($birthday);
        if (!$age) {
            return 0;
        }
        list($y1, $m1, $d1) = explode("-", date("Y-m-d", $age));
        list($y2, $m2, $d2) = explode("-", date("Y-m-d"), time());
        $age = $y2 - $y1;
        if (intval($m2 . $d2) < intval($m1 . $d1)) {
            $age -= 1;
        }
        return $age;
    }

    /**
     * 返回当前的毫秒时间戳
     */
    public static function microtime()
    {
        return round(microtime(true) * 1000);
    }
    // 字符串转二维数组
    public static function text2array($text, $endSplitor = "\n", $middleSplitor = '=')
    {
        $text = trim($text);
        $data = [];
        if (!$text) {
            return $data;
        }
        $arr = array_filter(explode($endSplitor, $text));
        foreach ($arr as $row) {
            $pair = explode($middleSplitor, $row, 2);
            $data[trim($pair[0])] = trim($pair[1]);
        }
        return $data;
    }

    /**
     * 不转义拼凑请求参数
     */
    public static function joinParams(array $params, string $arg_separator = "&"): string
    {
        $data = [];
        foreach ($params as $k => $v) {
            $data[] = "{$k}={$v}";
        }
        return implode($arg_separator, $data);
    }
    /**
     * HTML 内容转换为纯文本
     * 例如：html2text($content,['/[\*|？|！|。|，]+/']);
     * @param string
     * @return
     */
    public static function html2text($content, $expattern = [])
    {
        // 将 html 实体符号（&nbsp; 等）还原成 html 标签 -> 过滤首尾空白字符 -> 过滤 html 标签
        $content = strip_tags(trim(html_entity_decode($content)));
        $pattern = [
            '/\s/', // 匹配任何空白字符，包括空格、制表符、换页符等，等价于 [\f\n\r\t\v]
        ];
        if ($expattern) {
            $pattern = array_merge($pattern, $expattern);
        }
        return preg_replace($pattern, '', $content);
    }

    /**
     * 驼峰名称转换为下划线或短横线名称
     * @param string $name 类名称
     */
    public static function camelCase2UnderScoreCase($name, $splitor = '_')
    {
        if (!$name) {
            return $name;
        }
        $chars = str_split($name);
        array_walk($chars, function (&$v, $i) use ($splitor) {
            $ord = ord($v);
            if ($ord <= 90 && $ord >= 65) {
                $v = strtolower($v);
                if ($i > 0) {
                    $v = $splitor . $v;
                }
            }
        });
        return implode('', $chars);
    }
    /**
     * 下划线或短横线名称转换为驼峰名称
     * @param string $name 数据表名称
     */
    public static function underScoreCase2CamelCase($name, $splitor = '_')
    {
        return str_replace(' ', '', ucwords(str_replace($splitor, ' ', $name)));
    }

    /**
     * 类名转换为数据表名
     * @param string $name 类名称
     */
    public static function classname2table($name)
    {
        return self::camelCase2UnderScoreCase($name, '_');
    }

    /**
     * 数据表名称转换为类名
     * @param string $table 数据表名称
     */
    public static function table2classname($table)
    {
        return self::underScoreCase2CamelCase($table, '_');
    }

    /**
     * 获取数组中指定路径的值
     * @param array $array 数组
     * @param string $path 节点路径，例如：a.b.c
     * @param mixed $defVal 默认值
     * @return mixed 返回数组中的值
     */
    public static function getArrayValue(array $array, string $path, $defVal = null)
    {
        $paths = explode('.', $path);
        foreach ($paths as $node) {
            if (isset($array[$node])) {
                $array = $array[$node];
            } else {
                $array = $defVal;
                break;
            }
        }
        return $array;
    }
    /**
     * var_export() 方法的现代风格版
     * 说明：原始版：array()， 现代版：[]
     * @param mixed $var
     * @param string $indent 缩进内容，默认是空格
     * @param boolean $isCompact 是否紧凑模式，即数组是否换行
     * @return string
     */
    public static function varExport($var, $indent = '', $isCompact = false)
    {
        switch (gettype($var)) {
            case "string":
                return '\'' . addcslashes($var, "\\\$\"\r\n\t\v\f") . '\'';
            case "array":
                if (count($var) == 0) {
                    return '[]';
                }
                $newLine = $isCompact ? '' : "\n";
                $itemSpace = $isCompact ? "$indent " : "$indent    ";
                $indexed = array_keys($var) === range(0, count($var) - 1);
                $r = [];
                foreach ($var as $key => $value) {
                    $r[] = $itemSpace . ($indexed ? "" : self::varExport($key) . " => ") . self::varExport($value, $itemSpace);
                }
                return "[$newLine" . implode(",$newLine", $r) . "$newLine" . $indent . "]";
            case "boolean":
                return $var ? "TRUE" : "FALSE";
            default:
                return var_export($var, true);
        }
    }

    /**
     * 返回服务器 IP
     * 参考：SERVER_NAME 和 HTTP_HOST 的区别 (http://blog.sina.com.cn/s/blog_6d96d3160100q39x.html)
     * @version 1.0 2017-10-13 Added.
     */
    public static function getHostIP()
    {
        //return $_SERVER['HTTP_HOST']; // 有服务器域名则优先返回域名，否则返回服务器 ip
        return gethostbyname($_SERVER['SERVER_NAME']); // 返回服务器IP
    }

    /**
     * 获取数组分页数据
     */
    public static function arrayPaginate(array $data, int $page, int $pageSize): array
    {
        $start = ($page - 1) * $pageSize;
        $end = $start + $pageSize;
        $total = count($data);
        $list = [];
        for ($i = $start; $i < $end; $i++) {
            if ($i >= $total) {
                break;
            }
            $list[] = $data[$i];
        }
        return $list;
    }
}
